#include "program.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include <string.h>
#include <libgen.h>
#include "client.h"
#include "server.h"
#include "test.h"
#include "logcat.h"

/** Specifies if the program is running in debug mode.*/
int debug_flag;

/** Specifies if we got the sigusr signal in the test mode.*/
int usr_interrupt;

/**
 * Prints usage of the program, that means all the options recognized by the program.
 */
void print_usage() {
	printf("Usage:\n");
	printf("-s = runs this program as tftp server\n\tif it's not present, then this program starts as a client\n");
	printf("-a = specifies address of the server to connect to and can be present only if program is started as a client\n");
	printf("-p <value> = port on which the server runs or client connects to\n\tif not present, then standard 69 port is selected\n");
	printf("-d = tells program if it should print debug information\n");
	printf("-w <value> = writes given file to the server\n");
	printf("-r <value> = reads given file from server\n");
	printf("-h <value> = specifies home directory for the server\n");
	printf("-n = specifies mode for the transfer as netascii otherwise it is octet\n");
	printf("-f <value> = if write request then specifies the name of the file on the server\n\tif read request then specifies the name of the file on the client\n");
	printf("-u = prints this usage\n");
	printf("-t = runs tests and exits the program\n");
}

/**
 * Handler for the SIGUSR used in tests.
 */
void usr_signal(int sig) {
	usr_interrupt = 1;
}

/**
 * Registers handler for the SIGUSR used in tests.
 */
void register_usr_signal_handler() {
	struct sigaction sa;
	sa.sa_handler = usr_signal;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	if (sigaction(SIGUSR1, &sa, 0) == -1) {
		LOGE("Could not register signal handler");
		exit(EXIT_FAILURE);
	}
}

/**
 * Main function which handles all the options and calls appropriate server or client functions.
 */
int tftp_run(int argc, char *argv[]) {
	int server_flag = 0;
	char * address = 0;
	char * port = NULL;
	int write_flag = 0;
	int read_flag = 0;
	char * file_name = NULL;
	int home_flag = 0;
	char * home = NULL;
	int netascii_flag = 0;
	int file_flag = 0;
	char * file = NULL;
	int test_flag = 0;

	opterr = 0;

	int c;

	if (argc == 1) { //no arguments
		print_usage();
        return EXIT_FAILURE;
	}

    LOGE("argc=%d", argc);
    while ((c = getopt(argc, argv, "sp:dw:r:h:nf:ua:t")) != -1) {
        LOGE("c=%c", c);
		switch (c) {
			case 's':
				server_flag = 1;
				break;
			case 'p':
				port = optarg;
				break;
			case 'd':
				debug_flag = 1;
				break;
			case 'w':
				write_flag = 1;
				file = optarg;
				break;
			case 'r':
				read_flag = 1;
				file = optarg;
				break;
			case 'h':
				home_flag = 1;
				home = optarg;
				break;
			case 'n':
				netascii_flag = 1;
				break;
			case 'f':
				file_flag = 1;
				file_name = optarg;
				break;
			case 'u':
				print_usage();
				break;
			case 'a':
				address = optarg;
				break;
			case 't':
				test_flag = 1;
				break;
			case '?':
				if (optopt == 'p' || optopt == 'w' || optopt == 'r' || optopt == 'h' || optopt == 'f') {
					LOGE("Option -%c requires an argument.\n", optopt);
				} else if (isprint(optopt)) {
					LOGE("Unknown option `-%c'.\n", optopt);
				} else {
					LOGE("Unknown option character `\\x%x'.\n", optopt);
				}
                return EXIT_FAILURE;
			default:
				abort();
		}
	}

	if (test_flag) { //run tests
		register_usr_signal_handler();
		file_already_exists_test();
		file_not_found_test();
		correct_sent_test();
        LOGI("All tests passed correctly.\n");
        return EXIT_FAILURE; //maybe run tests and then continue?
	}

	if (server_flag && (write_flag || read_flag)) {
		LOGE("Cannot both start server and satisfy write or read requests.\n");
        return EXIT_FAILURE;
	} else if (write_flag && read_flag) {
		LOGE("Cannot satisfy write and read requests at the same time.\n");
        return EXIT_FAILURE;
	} else if (server_flag && file_flag) {
		LOGE("Cannot start server with -f option.\n");
        return EXIT_FAILURE;
	} else if (home_flag && (write_flag || read_flag)) {
		LOGE("Cannot set -h option for client.\n");
        return EXIT_FAILURE;
	} else if (server_flag && address != NULL) {
		LOGE("Cannot set -a option for server.\n");
        return EXIT_FAILURE;
	}

	if (home_flag && !does_dir_exist(home)) {
		LOGE("Specified home directory %s is not a directory.\n", home);
        return EXIT_FAILURE;
	}

    LOGE("server_flag:%d, write_flag:%d, read_flag:%d", server_flag, write_flag, read_flag);

	if (!server_flag && !write_flag && !read_flag) {
		LOGE("No request passed – read or write file\n");
        return EXIT_FAILURE;
	}

	if (port == NULL) { //if the user did not specify port, then use the default tftp port number
		port = "69";
	}

	if (server_flag) {
		start_server(port, home, 0);
        return EXIT_SUCCESS;
	} else { //we are client
		struct addrinfo *res = NULL;
		struct addrinfo *resorig = NULL;

		int sock = get_socket(address, &res, &resorig, port);
		mode_type mode = OCTET; //default mode
		if (netascii_flag) {
			mode = NETASCII;
		}
		if (file_name == NULL && write_flag) {
			file_name = basename(file); //takes the basename of the file
		} else if (file_name == NULL) {
			file_name = file;
		}
		int ret_val;
		if (write_flag) {
			ret_val = send_file(sock, file_name, res, file, mode);
		} else { //read req
			ret_val = read_file(sock, file, res, file_name, mode);
		}
		free(resorig);
		close(sock);
		return (ret_val);
	}
}

int upload(char *host, char *remoteFile, char *localFile) {
    struct addrinfo *res = NULL;
    struct addrinfo *resorig = NULL;

    int sock = get_socket(host, &res, &resorig, "69");
    mode_type mode = OCTET; //default mode
    if (remoteFile == NULL) {
        remoteFile = basename(localFile); //takes the basename of the file
    }
    int ret_val = send_file(sock, remoteFile, res, localFile, mode);
    free(resorig);
    close(sock);
    return (ret_val);
}


int download(char *host, char *remoteFile, char *localFile) {
    struct addrinfo *res = NULL;
    struct addrinfo *resorig = NULL;

    int sock = get_socket(host, &res, &resorig, "69");
    mode_type mode = OCTET; //default mode
    if (localFile == NULL) {
		localFile = remoteFile;
    }
    int ret_val = read_file(sock, remoteFile, res, localFile, mode);
    free(resorig);
    close(sock);
    return (ret_val);
}
